﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using GBPVRX2;
using GBPVR.Public;
using GBPVRLibrary.UtilityLibrary;

namespace GBPVRLibrary.ControlLibrary
{
    public abstract class GuiElement: IDisposable
    {
        public enum AnimationType
        {
            None = 0,
            Fade = 1,
            Stretch = 2,
            Glow = 4,
            SlideRight = 8,
            SlideLeft = 16,
            HideAfterAnimation = 1024
        }

        public bool IsAnimating { get { return (Animation & AnimationType.None) != AnimationType.None && TimeShown >= DateTime.Now.AddSeconds(-AnimationDuration); } }
        private const float AnimationDuration = 0.5f; //seconds

        public GuiElement()
        {
            this.Visible = true;
            TimeShown = DateTime.MinValue;
        }

        public void RenderElement(ArrayList RenderList, ref bool RequiresMoreRendering)
        {
            if (this.Visible)
            {
                if (this.Visible && TimeShown <= DateTime.Now.AddSeconds(-AnimationDuration) && (Animation & AnimationType.HideAfterAnimation) == AnimationType.HideAfterAnimation)
                {
                    this.Visible = false;
                    return;
                }
                ArrayList rl = new ArrayList(); // we use a seperate list so we only pass the elements of THIS to the post render method

                Render(rl, ref RequiresMoreRendering);
                Animate(rl);
                PostRender(rl);
                RenderList.AddRange(rl);
            }
        }

        public AnimationType Animation { get; set; }
        protected DateTime TimeShown { get; set; }

        public float AnimationSlideAmount { get; set; }

        protected abstract void Render(ArrayList RenderList, ref bool RequiresMoreRendering);

        public static List<GBPVRUiElement> RenderedElements = new List<GBPVRUiElement>();
        internal static Dictionary<GBPVRUiElement, GBPVRUiElementAttributes> RenderElements = new Dictionary<GBPVRUiElement, GBPVRUiElementAttributes>();
        
        private void Animate(ArrayList RenderList)
        {
            foreach (GBPVRUiElement el in RenderList)
            {
                if (el is GBPVRUiElementNonAnimating)
                    continue;
                if (!RenderElements.ContainsKey(el))
                    RenderElements.Add(el, new GBPVRUiElementAttributes() { Alpha = el.alpha, Rect = el.rect, TextureBottomLeft = el.textureBottomLeft, TextureBottomRight = el.textureBottomRight, TextureTopLeft = el.textureTopLeft, TextureTopRight = el.textureTopRight });
                else if(!RenderedElements.Contains(el))
                {
                    GBPVRUiElementAttributes o = RenderElements[el];
                    el.textureTopLeft = o.TextureTopLeft;
                    el.textureBottomLeft = o.TextureBottomLeft;
                    el.textureTopRight = o.TextureTopRight;
                    el.textureBottomRight = o.TextureBottomRight;
                    el.alpha = o.Alpha;
                    RenderedElements.Add(el);
                }
            }

            lock (RenderElements)
            {
                if (TimeShown > DateTime.Now.AddSeconds(-AnimationDuration))
                {
                    float diff = (float)(DateTime.Now - TimeShown).TotalMilliseconds;
                    diff /= (AnimationDuration * 1000); // 0.5 seconds;
                    float stretch = 0.15f;
                    // animate
                    foreach (GBPVRUiElement el in RenderList)
                    {
                        if (el is GBPVRUiElementNonAnimating)
                            continue;

                        GBPVRUiElementAttributes o = RenderElements[el];
                        if ((Animation & AnimationType.Fade) == AnimationType.Fade)
                        {
                            el.alpha = (int)(o.Alpha * diff);
                        }

                        if ((Animation & AnimationType.Stretch) == AnimationType.Stretch)
                        {
                            StretchGBPVRUiElement(el, stretch, diff);
                        }

                        if ((Animation & AnimationType.SlideLeft) == AnimationType.SlideLeft)
                        {
                            SlideGBPVRUiElement(el, AnimationSlideAmount, diff, true);
                        }
                        if ((Animation & AnimationType.SlideRight) == AnimationType.SlideRight)
                        {
                            SlideGBPVRUiElement(el, AnimationSlideAmount, diff, false);
                        }
                    }
                    GuiElement.NeedsRendering = true;
                }
                else
                {

                    foreach (GBPVRUiElement el in RenderList)
                    {
                        if ((Animation & AnimationType.SlideLeft) == AnimationType.SlideLeft)
                        {
                            SlideGBPVRUiElement(el, AnimationSlideAmount, 1, true);
                        }
                        if ((Animation & AnimationType.SlideRight) == AnimationType.SlideRight)
                        {
                            SlideGBPVRUiElement(el, AnimationSlideAmount, 1, false);
                        }
                    }
                }
            }
        }

        public static void StretchGBPVRUiElement(GBPVRUiElement Element, float MaxStretch, float Percent)
        {
            Element.textureTopLeft.X = -MaxStretch + (MaxStretch * Percent);
            Element.textureBottomLeft.X = -MaxStretch + (MaxStretch * Percent);
            Element.textureTopRight.X = (1f + MaxStretch) - (MaxStretch * Percent);
            Element.textureBottomRight.X = (1f + MaxStretch) - (MaxStretch * Percent);

            Element.textureTopLeft.Y = -MaxStretch + (MaxStretch * Percent);
            Element.textureTopRight.Y = -MaxStretch + (MaxStretch * Percent);
            Element.textureBottomLeft.Y = (1f + MaxStretch) - (MaxStretch * Percent);
            Element.textureBottomRight.Y = (1f + MaxStretch) - (MaxStretch * Percent);

        }

        public static void SlideGBPVRUiElement(GBPVRUiElement Element, float SlideAmount, float Percent, bool Left)
        {            
            // original rectangles is good, but for our selected items in the list,
            // we use one object, and manually set that rectangle elsewhere, so setting it 
            // back to the original is screwing it up.....
            if (!OriginalRectangles.ContainsKey(Element))
                OriginalRectangles[Element] = Element.rect;
            RectangleF rect = OriginalRectangles[Element];
            //float SlideAmount = rect.Width + 10;
            if (Left)
                rect.X += SlideAmount - (SlideAmount * Percent);
            else
                rect.X += (SlideAmount * Percent);
            Element.SetRect(rect);
        }

        internal static Dictionary<object, RectangleF> OriginalRectangles = new Dictionary<object, RectangleF>();

        /// <summary>
        /// Called directly after Render, this is the place to do animating since the renderlist is built
        /// </summary>
        /// <param name="RenderList">all the elements being rendered</param>
        protected virtual void PostRender(ArrayList RenderList) 
        { 
        }


        public RectangleF Location { get; set; }
        /// <summary>
        /// Gets or sets if this GuiElemen has focus
        /// </summary>
        public virtual bool HasFocus { get; set; }

        private bool _Visible;
        /// <summary>
        /// Gets or sets if this is visible or not
        /// </summary>
        public bool Visible
        {
            get
            {
                return _Visible;
            }
            set
            {
                _Visible = value;
                if (value)
                {
                    TimeShown = DateTime.Now;
                }
            }
        }

        /// <summary>
        /// Called to let plugins handle key press events
        /// </summary>        
        /// <returns>true when key resulted in change of state requiring screen update</returns>
        public virtual bool OnKeyDown(KeyEventArgs e)
        {
            return false;
        }

        /// <summary>
        /// called to notify the plugin of mouse wheel events.
        /// </summary>
        /// <param name="e"></param>
        public virtual void OnMouseWheel(MouseEventArgs e)
        {
            // most plugins check the delta value and translate this into a key up/down event, then
            // call OnKeyDown().
        }

        public virtual bool OnUiClick(System.Drawing.PointF location)
        {
            return false;
        }

        public virtual bool OnUiDoubleClick(System.Drawing.PointF location)
        {
            return false;
        }


        private static bool _NeedsRendering = false;
        private static SkinHelper2 _SkinHelper;

        #region Properties
        /// <summary>
        /// Gets or sets if the element needs rendering
        /// </summary>
        public static bool NeedsRendering { get { if (_NeedsRendering) { _NeedsRendering = false; return true; } return _NeedsRendering; } set { _NeedsRendering = value; } }

        /// <summary>
        /// Gets or sets the base skin helper for 'Reven' plugins
        /// </summary>
        public static SkinHelper2 SkinHelper
        {
            get
            {
                if (_SkinHelper == null)
                    _SkinHelper = new SkinHelper2(@"GBPVRLibrary\skin.xml");
                return _SkinHelper;
            }
        }

        public static IPluginHelper PluginHelper { get { return PluginHelperFactory.getPluginHelper(); } }

        public static string PluginName { get; set; }

        #endregion

        #region helper methods
        protected RectangleF ParseRectangle(string Bounds)
        {
            string[] parts = Bounds.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length == 4)
                return new RectangleF(float.Parse(parts[0]), float.Parse(parts[1]), float.Parse(parts[2]), float.Parse(parts[3]));
            else if (parts.Length == 2)
                return new RectangleF(0, 0, float.Parse(parts[0]), float.Parse(parts[1]));
            return new RectangleF();
        }


        public static SizeF? ParseSize(string Size)
        {
            if (String.IsNullOrEmpty(Size))
                return null;
            string[] parts = Size.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length != 2)
                return null;
            return new SizeF(float.Parse(parts[0]), float.Parse(parts[1]));
        }
        public static PointF? ParseLocation(string Location)
        {
            if (String.IsNullOrEmpty(Location))
                return null;
            string[] parts = Location.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length != 2)
                return null;
            return new PointF(float.Parse(parts[0]), float.Parse(parts[1]));
        }

        #endregion

        #region IDisposable Members

        public virtual void Dispose()
        {
        }

        #endregion

        public class GBPVRUiElementAttributes 
        {
            public GBPVRUiElementAttributes() { }

            public GBPVRUiElementAttributes(GBPVRUiElement Element)
            {
                this.Alpha = Element.alpha;
                this.Rect = Element.rect;
                this.TextureBottomLeft = Element.textureBottomLeft;
                this.TextureBottomRight = Element.textureBottomRight;
                this.TextureTopLeft = Element.textureTopLeft;
                this.TextureTopRight = Element.textureTopRight;
            }
            public int Alpha; 
            public RectangleF Rect;
            public PointF TextureBottomLeft;
            public PointF TextureBottomRight;
            public PointF TextureTopLeft;
            public PointF TextureTopRight;
        }
    }
}
